package log

import (
	"bytes"
	"chainproxy/config"
	"fmt"
	nested "github.com/antonfisher/nested-logrus-formatter"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"os"
	"sort"
	"strings"
	"time"
)

type MyNestedFormatter struct {
	nested.Formatter
}

var Logger = logrus.New()

func InitLog()  {
	f := config.GetString("log.format")
	var ft logrus.Formatter
	if f == "json"{
		ft = &logrus.JSONFormatter{}
	}else {
		//ft = &logrus.TextFormatter{DisableColors:false, TimestampFormat:"2006-01-02 15:04:05.000"}
		//ft = &nested.Formatter{HideKeys:false, TimestampFormat:"2006-01-02 15:04:05.000"}
		var f MyNestedFormatter
		f.TimestampFormat = "2006-01-02 15:04:05.000"
		ft = &f
	}

	Logger.SetReportCaller(true)
	Logger.SetFormatter(ft)
	lv := config.GetString("log.level")
	Logger.SetOutput(os.Stdout)
	level,_ := logrus.ParseLevel(lv)
	Logger.SetLevel(level)
	Logger.AddHook(newLfsHook(lv, ft))
}

//切割日志文件
func newLfsHook(logLevel string, format logrus.Formatter) logrus.Hook {
	var logName = config.GetString("log.file")
	var pat = config.GetString("log.pat")
	fmt.Println(logName)
	var opts []rotatelogs.Option
	if config.GetString("system") == "linux"{
		// WithLinkName为最新的日志建立软连接，以方便随着找到当前日志文件
		opts = append(opts, rotatelogs.WithLinkName(logName))
	}
	opts = append(opts, rotatelogs.WithRotationTime(config.GetDuration("log.rt")))
	opts = append(opts, rotatelogs.WithRotationCount(config.GetUint("log.rc")))
	opts = append(opts, rotatelogs.WithClock(rotatelogs.Local))

	writer, err := rotatelogs.New(
		logName + pat, opts...
	)

	if err != nil {
		Logger.Errorf("config local file system for logger error: %v", err)
	}

	level, err := logrus.ParseLevel(logLevel)

	if err == nil {
		Logger.SetLevel(level)
	} else {
		Logger.SetLevel(logrus.InfoLevel)
	}

	lfsHook := lfshook.NewHook(lfshook.WriterMap{
		logrus.DebugLevel: writer,
		logrus.InfoLevel:  writer,
		logrus.WarnLevel:  writer,
		logrus.ErrorLevel: writer,
		logrus.FatalLevel: writer,
		logrus.PanicLevel: writer,
	}, format)

	return lfsHook
}

func (f *MyNestedFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	levelColor := getColorByLevel(entry.Level)

	timestampFormat := f.TimestampFormat
	if timestampFormat == "" {
		timestampFormat = time.StampMilli
	}

	// output buffer
	b := &bytes.Buffer{}

	// write time
	b.WriteString(entry.Time.Format(timestampFormat))

	// write level
	level := strings.ToUpper(entry.Level.String())

	if !f.NoColors {
		fmt.Fprintf(b, "\x1b[%dm", levelColor)
	}

	b.WriteString(" [")
	if f.ShowFullLevel {
		b.WriteString(level)
	} else {
		b.WriteString(level[:4])
	}
	b.WriteString("] ")

	if !f.NoColors && f.NoFieldsColors {
		b.WriteString("\x1b[0m")
	}

	//write caller
	if entry.Logger.ReportCaller{
		if entry.Caller != nil {
			s := fmt.Sprintf("%s %s.%d\n", entry.Caller.Function, entry.Caller.File, entry.Caller.Line)
			b.WriteString(s)
		}
		//else {
		//	fmt.Fprintf(b, "\x1b[%dm", colorRed)
		//	b.WriteString("Failed to display function name, file name, line number.\n")
		//	b.WriteString("\x1b[0m")
		//}
	}

	// write fields
	if f.FieldsOrder == nil {
		f.writeFields(b, entry)
	} else {
		f.writeOrderedFields(b, entry)
	}

	if !f.NoColors && !f.NoFieldsColors {
		b.WriteString("\x1b[0m")
	}

	// write message
	if f.TrimMessages {
		b.WriteString(strings.TrimSpace(entry.Message))
	} else {
		b.WriteString(entry.Message)
	}
	b.WriteByte('\n')

	return b.Bytes(), nil
}

func (f *MyNestedFormatter) writeFields(b *bytes.Buffer, entry *logrus.Entry) {
	if len(entry.Data) != 0 {
		fields := make([]string, 0, len(entry.Data))
		for field := range entry.Data {
			fields = append(fields, field)
		}

		sort.Strings(fields)

		for _, field := range fields {
			f.writeField(b, entry, field)
		}
	}
}

func (f *MyNestedFormatter) writeOrderedFields(b *bytes.Buffer, entry *logrus.Entry) {
	length := len(entry.Data)
	foundFieldsMap := map[string]bool{}
	for _, field := range f.FieldsOrder {
		if _, ok := entry.Data[field]; ok {
			foundFieldsMap[field] = true
			length--
			f.writeField(b, entry, field)
		}
	}

	if length > 0 {
		notFoundFields := make([]string, 0, length)
		for field := range entry.Data {
			if foundFieldsMap[field] == false {
				notFoundFields = append(notFoundFields, field)
			}
		}

		sort.Strings(notFoundFields)

		for _, field := range notFoundFields {
			f.writeField(b, entry, field)
		}
	}
}

func (f *MyNestedFormatter) writeField(b *bytes.Buffer, entry *logrus.Entry, field string) {
	if f.HideKeys {
		fmt.Fprintf(b, "[%v] ", entry.Data[field])
	} else {
		fmt.Fprintf(b, "[%s:%v] ", field, entry.Data[field])
	}
}

const (
	colorRed    = 31
	colorYellow = 33
	colorBlue   = 36
	colorGray   = 37
)

func getColorByLevel(level logrus.Level) int {
	switch level {
	case logrus.DebugLevel:
		return colorGray
	case logrus.WarnLevel:
		return colorYellow
	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
		return colorRed
	default:
		return colorBlue
	}
}